package com.effectiv.gooruda.rest.api.service;

import java.util.ArrayList;
import java.util.List;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.mongodb.core.MongoTemplate;

import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;

import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.stereotype.Component;

import com.effectiv.gooruda.rest.api.domain.Profile;
import com.effectiv.gooruda.rest.api.domain.Role;
import com.effectiv.gooruda.rest.api.domain.User;
import com.effectiv.gooruda.rest.api.exception.DuplicateDataException;
import com.effectiv.gooruda.rest.api.exception.FailedAuthenticationException;
import com.effectiv.gooruda.rest.api.exception.NoDataFoundException;
import com.effectiv.gooruda.rest.api.operation.RoleOperations;
import com.effectiv.gooruda.rest.api.operation.SessionOperations;
import com.effectiv.gooruda.rest.api.operation.UserOperations;
import com.effectiv.gooruda.security.rest.RESTSecurityUtil;
@Deprecated
@Slf4j
@Component
public class UserServiceImpl implements UserOperations {

	private final String USER_COLLECTION_BASE = "t_app_user_";

	@Autowired
	private MongoTemplate template;

	@Autowired
	@Qualifier("roleServiceImpl")
	private RoleOperations roleOperations;
	
	@Autowired
	@Qualifier("sessionServiceImpl")
	private SessionOperations sessionOperations;

	@Autowired
	@Qualifier("passwordEncoder")
	private PasswordEncoder passwordEncoder;

	
	public User save(User user) {
		String collection = USER_COLLECTION_BASE + RESTSecurityUtil.getAppId();
		String userName = user.getUserName();

		List<Role> roles = user.getRoles();

		user.setRoles(null);

		User u = findByUserName(userName);

		if (u != null) {
			throw new DuplicateDataException("User with user name - "
					+ userName + " already exists.");
		}

		String password = passwordEncoder.encodePassword(user.getPassword(),
				null);
		user.setPassword(password);

		template.save(user, collection);

		if (roles != null) {
			List<String> roleNames = new ArrayList<String>();

			for (Role r : roles) {
				roleNames.add(r.getName());
			}

			user = addRoles(userName, roleNames);
		}

		return user;
	}

	public User addRoles(String userName, List<String> roles) {
		User u = findByUserName(userName);

		if (u == null) {
			throw new NoDataFoundException("User with name - " + userName
					+ " not found.");
		}

		List<Role> existingRoles = u.getRoles();

		if (existingRoles == null) {
			existingRoles = new ArrayList<Role>();
		}

		for (String r : roles) {
			Role role = roleOperations.findByName(r);

			log.debug("Retrieved role = {}", role);

			if (role == null) {
				throw new NoDataFoundException("No role found for role name - "
						+ r);
			}

			if (existingRoles.contains(role)) {
				log.debug("Role already attached to user = {}", role);
				throw new DuplicateDataException("Role - " + r
						+ " already associated with user - " + userName);
			}
			existingRoles.add(role);

		}

		log.debug("Roles before saving = {}", existingRoles);

		u.setRoles(existingRoles);

		template.save(u, USER_COLLECTION_BASE + RESTSecurityUtil.getAppId());

		return u;
	}

	
	public User addRole(String userName, String role) {
		// check if role exists
		Role r = roleOperations.findByName(role);

		if (r == null) {
			throw new NoDataFoundException("Role with name - " + role
					+ " not found.");
		}

		User u = findByUserName(userName);

		if (u == null) {
			throw new NoDataFoundException("User with name - " + userName
					+ " not found.");
		}

		List<Role> roles = u.getRoles();
		if (roles == null) {
			roles = new ArrayList<Role>();
		}

		if (roles.contains(r)) {
			throw new DuplicateDataException("Role - " + role
					+ " already attached to user - " + userName);
		}

		roles.add(r);
		u.setRoles(roles);

		template.save(u, USER_COLLECTION_BASE + RESTSecurityUtil.getAppId());

		return u;
	}

	
	public User findByUserName(String userName) {
		Query query = new Query();
		query.addCriteria(Criteria.where("userName").is(userName));

		User u = template.findOne(query, User.class, USER_COLLECTION_BASE
				+ RESTSecurityUtil.getAppId());

		return u;
	}

	
	public User authenticate(String userName, String password) {
		User u = findByUserName(userName);

		if (u == null) {
			throw new FailedAuthenticationException("Invalid user name - "
					+ userName);
		}

		if (!passwordEncoder.isPasswordValid(u.getPassword(), password, null)) {
			throw new FailedAuthenticationException("Invalid password.");
		}

		return u;
	}

	
	public User changePassword(String userName, String oldPassword,
			String newPassword) {
		User user = authenticate(userName, oldPassword);

		String password = passwordEncoder.encodePassword(newPassword, null);

		user.setPassword(password);

		template.save(user, USER_COLLECTION_BASE + RESTSecurityUtil.getAppId());

		return user;
	}

	
	public User authenticateAndStartSession(String userName, String password) {
		User user = authenticate(userName, password);
		sessionOperations.create(user);
		return user;
	}

	
	public User addOrUpdateProfile(String userName, Profile profile) {
		User user = findByUserName(userName);
		user.setProfile(profile);
		template.save(user, USER_COLLECTION_BASE + RESTSecurityUtil.getAppId());
		
		return user;
	}

	
	public void deleteUser(String userName) {
		Query query = new Query();
		query.addCriteria(Criteria.where("userName").is(userName));
		
		template.remove(query, USER_COLLECTION_BASE + RESTSecurityUtil.getAppId());
	}

	
	public List<User> findAll(int pageNo, int pageSize) {
		Query query = new Query();
		query.skip(pageNo * pageSize);
		query.limit(pageSize);
		return template.find(query, User.class, USER_COLLECTION_BASE + RESTSecurityUtil.getAppId());
	}

	
	public long count() {
		return template.count(new Query(), USER_COLLECTION_BASE + RESTSecurityUtil.getAppId());
		
	}

	
	public List<User> findLockedUsers(int pageNo, int pageSize) {
		Query query = new Query();
		query.addCriteria(Criteria.where("locked").is(true));
		query.skip(pageNo * pageSize);
		query.limit(pageSize);
		return template.find(query, User.class, USER_COLLECTION_BASE + RESTSecurityUtil.getAppId());
	}

	
	public long countLockedUsers() {
		Query query = new Query();
		query.addCriteria(Criteria.where("locked").is(true));
		return template.count(query, USER_COLLECTION_BASE + RESTSecurityUtil.getAppId());
	}

	
	public List<Role> findRolesForUser(String userName) {
		User u = findByUserName(userName);
		return u.getRoles();
	}

	
	public User findOne(String id) {
		return template.findById(id, User.class, USER_COLLECTION_BASE + RESTSecurityUtil.getAppId());
		
	}

	
	public User findByEmail(String email) {
		Query query = new Query();
		query.addCriteria(Criteria.where("email").is(email));
		return template.findOne(query, User.class, USER_COLLECTION_BASE + RESTSecurityUtil.getAppId());
	}

	
	public User lock(String userName) {
		User u = findByUserName(userName);
		u.setLocked(true);
		template.save(u, USER_COLLECTION_BASE + RESTSecurityUtil.getAppId());
		return u;
	}

	
	public User unlock(String userName) {
		User u = findByUserName(userName);
		u.setLocked(false);
		template.save(u, USER_COLLECTION_BASE + RESTSecurityUtil.getAppId());
		return u;
	}

	
	public User removeAllRoles(String userName) {
		User u = findByUserName(userName);
		u.setRoles(null);
		template.save(u, USER_COLLECTION_BASE + RESTSecurityUtil.getAppId());
		return u;
	}

	
	public User updateEmail(String userName, String newEmail) {
		User u = findByUserName(userName);
		u.setEmail(newEmail);
		template.save(u, USER_COLLECTION_BASE + RESTSecurityUtil.getAppId());
		return u;
	}

}
